/*
Copyright (c) 2017-2019, 武汉市嘉仪尔科技有限公司（Wuhan jiayier Technology Co.,Ltd)

Email: 707777212@qq.com
Web: www.o2mation.cn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef VAL_H
#define VAL_H

#include <stdint.h>
#include <stddef.h>

typedef uint64_t val_t;

typedef union {
    val_t   v;
    double  d;
} valnum_t;

#define MAKE_TAG(s, t) \
  ((val_t)(s) << 63 | (val_t) 0x7ff0 <<48 | (val_t)(t) <<48)

enum GC_TYPE
{
    GC_NONE,
    GC_ROOT,
    GC_OBJECT,
    GC_DICT,
    GC_CLASS,
    GC_NATIVE_CLASS,
    GC_SET,
    GC_MARKED,
    GC_FUNC,
    GC_LIST,
    GC_BUFFER,
    GC_LOCAL,
    GC_STRING,
    GC_TUPLE,
    GC_MODULE,
    GC_MEMBER,
};

enum VAL_TYPE
{
    TYPE_NUM = 0,
    TYPE_STR_H,
    TYPE_STR_F,
    TYPE_BOOL,
    TYPE_FUNC,
    TYPE_FUNC_NATIVE,
    TYPE_LIST,
    TYPE_NULL,
    TYPE_UND,
    TYPE_BUFFER,
    TYPE_OBJ,
    TYPE_TUPLE,
    TYPE_CLASS,
    TYPE_GLOBAL,
    TYPE_FRAME,
    TYPE_FOREIGN,
};

#define TAG_INFINITE        MAKE_TAG(0, TYPE_NUM)
#define TAG_GLOBAL          MAKE_TAG(1, TYPE_GLOBAL)
#define TAG_FRAME           MAKE_TAG(1, TYPE_FRAME)
#define TAG_BUFFER          MAKE_TAG(1, TYPE_BUFFER)
#define TAG_STRING_H        MAKE_TAG(1, TYPE_STR_H)
#define TAG_STRING_F        MAKE_TAG(1, TYPE_STR_F)
#define TAG_BOOLEAN         MAKE_TAG(1, TYPE_BOOL)
#define TAG_FUNC_SCRIPT     MAKE_TAG(1, TYPE_FUNC)
#define TAG_FUNC_NATIVE     MAKE_TAG(1, TYPE_FUNC_NATIVE)

#define TAG_LIST            MAKE_TAG(1, TYPE_LIST)
#define TAG_TUPLE           MAKE_TAG(1, TYPE_TUPLE)

#define TAG_UNDEFINED       MAKE_TAG(1, TYPE_UND)
#define TAG_NULL            MAKE_TAG(1, TYPE_NULL)

#define TAG_OBJECT          MAKE_TAG(1, TYPE_OBJ)
#define TAG_FOREIGN         MAKE_TAG(1, TYPE_FOREIGN)
#define TAG_CLASS           MAKE_TAG(1, TYPE_CLASS)

#define NUM_MASK            MAKE_TAG(0, 0xF)
#define TAG_MASK            MAKE_TAG(1, 0xF)
#define VAL_MASK            (~MAKE_TAG(1, 0xF))

#define VAL_UNDEFINED       TAG_UNDEFINED
#define VAL_NULL            TAG_NULL
#define VAL_INFINITE        TAG_INFINITE
#define VAL_TRUE            (TAG_BOOLEAN | 1)
#define VAL_FALSE           (TAG_BOOLEAN)

static inline
int val_type(val_t *v) {
    int type = (*v) >> 48;
    if ((type & 0x7ff0) != 0x7ff0) {
        return 0;
    } else {
        return type & 0xf;
    }
}

static inline double val_2_double(val_t *v) {
    return ((valnum_t*)v)->d;
}

static inline unsigned int val_2_integer(val_t *v) {
    return (unsigned int)((valnum_t*)v)->d;
}

static inline intptr_t val_2_intptr(val_t *v) {
    return (intptr_t)(*v & VAL_MASK);
}

#define double_2_val(d) (((valnum_t*)&(d))->v)

static inline int val_is_number(val_t *v) {
    return (*v & TAG_INFINITE) != TAG_INFINITE;
}

static inline int val_is_integer(val_t *v){
    if(!val_is_number(v)) return 0;
    if(val_2_double(v) - val_2_integer(v) == 0) return 1;
    return 0;
}

static inline int val_is_heap_string(val_t *v) {
    return (*v & TAG_MASK) == TAG_STRING_H;
}

static inline int val_is_foreign_string(val_t *v) {
    return (*v & TAG_MASK) == TAG_STRING_F;
}

static inline int val_is_string(val_t *v) {
    return val_is_heap_string(v) || val_is_foreign_string(v);
}

static inline int val_is_boolean(val_t *v) {
    return (*v & TAG_MASK) == TAG_BOOLEAN;
}

static inline int val_is_global(val_t *v) {
    return (*v & TAG_MASK) == TAG_GLOBAL;
}

static inline int val_is_frame(val_t *v) {
    return (*v & TAG_MASK) == TAG_FRAME;
}

static inline int val_is_buffer(val_t *v) {
    return (*v & TAG_MASK) == TAG_BUFFER;
}

static inline int val_is_script(val_t *v) {
    return (*v & TAG_MASK) == TAG_FUNC_SCRIPT;
}

static inline int val_is_native(val_t *v) {
    return (*v & TAG_MASK) == TAG_FUNC_NATIVE;
}

static inline int val_is_list(val_t *v) {
    return (*v & TAG_MASK) == TAG_LIST;
}

static inline int val_is_tuple(val_t *v) {
    return (*v & TAG_MASK) == TAG_TUPLE;
}

static inline int val_is_function(val_t *v) {
    return val_is_script(v) || val_is_native(v);
}

static inline int val_is_undefined(val_t *v) {
    return (*v & TAG_MASK) == TAG_UNDEFINED;
}

static inline int val_is_null(val_t *v) {
    return (*v & TAG_MASK) == TAG_NULL;
}

static inline int val_is_object(val_t *v) {
    return (*v & TAG_MASK) == TAG_OBJECT;
}

static inline int val_is_class(val_t *v) {
    return (*v & TAG_MASK) == TAG_CLASS;
}

static inline int val_is_foreign(val_t *v) {
    return (*v & TAG_MASK) == TAG_FOREIGN;
}

static inline const char *val_2_cstring(val_t *v) {
    uint64_t t = *v & TAG_MASK;

    if (t == TAG_STRING_F) {
        return (const char *) val_2_intptr(v);
    } else
    if (t == TAG_STRING_H) {
        return (const char *) (val_2_intptr(v));
    } else {
        return NULL;
    }
}

static inline val_t val_mk_number(double d) {
    return double_2_val(d);
}

static inline val_t val_mk_foreign_string(intptr_t s) {
    return TAG_STRING_F | s;
}

static inline val_t val_mk_heap_string(intptr_t s) {
    return TAG_STRING_H | s;
}

static inline val_t val_mk_boolean(int v) {
    return TAG_BOOLEAN | (!!v);
}

static inline val_t val_mk_script(intptr_t s) {
    return TAG_FUNC_SCRIPT | s;
}

static inline val_t val_mk_native(intptr_t n) {
    return TAG_FUNC_NATIVE | n;
}

static inline val_t val_mk_null(void) {
    return TAG_NULL;
}

static inline val_t val_mk_undefined(void) {
    return TAG_UNDEFINED;
}

static inline val_t val_mk_object(void *ptr) {
    return TAG_OBJECT | (intptr_t) ptr;
}

static inline val_t val_mk_buffer(void *ptr) {
    return TAG_BUFFER | (intptr_t) ptr;
}

static inline val_t val_mk_list(void *ptr) {
    return TAG_LIST | (intptr_t) ptr;
}

static inline val_t val_mk_tuple(void *ptr) {
    return TAG_TUPLE | (intptr_t) ptr;
}

static inline val_t val_mk_global(int v) {
    return TAG_GLOBAL | v;
}

static inline val_t val_mk_nonlocal(int v) {
    return TAG_FRAME|v;
}

static inline val_t val_mk_class(void *ptr) {
    return TAG_CLASS | (intptr_t) ptr;
}

static inline val_t val_mk_foreign(void *ptr) {
    return TAG_FOREIGN | (intptr_t) ptr;
}

static inline void val_set_boolean(val_t *p, int b) {
    *((uint64_t *)p) = TAG_BOOLEAN | !!b;
}

static inline void val_set_number(val_t *p, double d) {
    *((double *)p) = d;
}

static inline void val_set_global(val_t *p, int v)
{
    *((uint64_t *)p) = TAG_GLOBAL|v;
}

static inline void val_set_frame(val_t *p, int v)
{
    *((uint64_t *)p) = TAG_FRAME|v;
}

static inline void val_set_foreign(val_t *p, intptr_t v)
{
    *((uint64_t *)p) = TAG_FOREIGN | v;
}

static inline void val_set_foreign_string(val_t *p, intptr_t s) {
    *((uint64_t *)p) = TAG_STRING_F | s;
}

static inline void val_set_heap_string(val_t *p, intptr_t s) {
    *((uint64_t *)p) = TAG_STRING_H | s;
}

static inline void val_set_script(val_t *p, intptr_t s) {
    *((uint64_t *)p) = TAG_FUNC_SCRIPT | s;
}

static inline void val_set_native(val_t *p, intptr_t f) {
    *((uint64_t *)p) = TAG_FUNC_NATIVE | f;
}

static inline void val_set_undefined(val_t *p) {
    *((uint64_t *)p) = TAG_UNDEFINED;
}

static inline void val_set_null(val_t *p) {
    *((uint64_t *)p) = TAG_NULL;
}

static inline void val_set_object(val_t *p, intptr_t d) {
    *((uint64_t *)p) = TAG_OBJECT | d;
}

static inline void val_set_class(val_t *p, intptr_t d) {
    *((uint64_t *)p) = TAG_CLASS | d;
}

static inline void val_set_buffer(val_t *p, void *b) {
    *((uint64_t *)p) = TAG_BUFFER | (intptr_t)b;
}

static inline void val_set_list(val_t *p, void *b) {
    *((uint64_t *)p) = TAG_LIST | (intptr_t)b;
}

int val_to_string(val_t * v, char *s);

#endif

